package com.lagou.ipersistence.executor.statement;

import com.lagou.ipersistence.executor.Executor;
import com.lagou.ipersistence.executor.ExecutorException;
import com.lagou.ipersistence.executor.parameter.ParameterHandler;
import com.lagou.ipersistence.executor.resultset.ResultSetHandler;
import com.lagou.ipersistence.mapping.BoundSql;
import com.lagou.ipersistence.mapping.MappedStatement;
import com.lagou.ipersistence.reflection.factory.ObjectFactory;
import com.lagou.ipersistence.session.Configuration;
import com.lagou.ipersistence.session.ResultHandler;
import com.lagou.ipersistence.session.RowBounds;
import com.lagou.ipersistence.type.TypeHandlerRegistry;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * @author Yan Jiahong
 * Created on 2020/11/22
 */
public abstract class BaseStatementHandler implements StatementHandler {
    protected final Configuration configuration;
    protected final ObjectFactory objectFactory;
    protected final TypeHandlerRegistry typeHandlerRegistry;
    protected final ResultSetHandler resultSetHandler;
    protected final ParameterHandler parameterHandler;
    
    protected final Executor executor;
    protected final MappedStatement mappedStatement;
    protected final RowBounds rowBounds;
    
    protected BoundSql boundSql;
    
    public BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject,
                                RowBounds rowBounds, ResultHandler<?> resultHandler, BoundSql boundSql) {
        this.configuration = mappedStatement.getConfiguration();
        
        this.executor = executor;
        this.mappedStatement = mappedStatement;
        this.rowBounds = rowBounds;
        
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        this.objectFactory = configuration.getObjectFactory();
        
        if (boundSql == null) {
            boundSql = mappedStatement.getBoundSql(parameterObject);
        }
        this.boundSql = boundSql;
        
        this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
        this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler,
                resultHandler, boundSql);
    }
    
    @Override
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        Statement stmt = null;
        try {
            stmt = instantiateStatement(connection);
            setStatementTimeout(stmt, transactionTimeout);
            return stmt;
        } catch (SQLException e) {
            closeStatement(stmt);
            throw new ExecutorException("解析statement时出错, 原因: " + e, e);
        }
    }
    
    protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
    
    protected void closeStatement(Statement stmt) {
        try {
            if (stmt != null) {
                stmt.close();
            }
        } catch (SQLException e) {
            // ignore
        }
    }
    
    protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {
        Integer queryTimeout = null;
        if (mappedStatement.getTimeout() != null) {
            queryTimeout = mappedStatement.getTimeout();
        } else if (configuration.getDefaultStatementTimeout() != null) {
            queryTimeout = configuration.getDefaultStatementTimeout();
        }
        if (queryTimeout != null) {
            stmt.setQueryTimeout(queryTimeout);
        }
    }
    
    @Override
    public BoundSql getBoundSql() {
        return boundSql;
    }
    
    @Override
    public ParameterHandler getParameterHandler() {
        return parameterHandler;
    }
}
